<?php
/**
 * Provides cryptographically secure random bytes generation
 *
 * Warning: Use SecureRandom instead of directly using this class!
 *
 * @copyright  Based on frostschutz's post at
 *             http://forums.thedailywtf.com/forums/t/16453.aspx
 * @copyright  Copyright (c) 2011 The Fajr authors (see AUTHORS).
 *             Use of this source code is governed by a MIT license that can be
 *             found in the LICENSE file in the project root directory.
 * @package    Fajr
 * @subpackage Security
 * @author     frostschutz
 * @author     Peter Perešíni <ppershing+fajr@gmail.com>
 * @filesource
 */
namespace fajr\security;

use fajr\libfajr\base\Preconditions;
use COM;

/**
 * Generate random bytes using windows cryptoapi.
 *
 * Warning: Use SecureRandom instead of directly using this class!
 *
 * @package    Fajr
 * @subpackage Security
 * @author     Peter Perešíni <ppershing+fajr@gmail.com>
 */
class SecureRandomWindowsCryptoapiProvider implements SecureRandomProvider {
  /**
   * Windows cryptoapi COM name.
   */
  const CRYPTOAPI_COM = 'CAPICOM.Utilities.1';

  /**
   * Check whether we can generate bytes using windows crypto api.
   *
   * @returns bool true if windows cryptoapi is available
   */
  public static function isAvailable() {
    if (!version_compare(PHP_VERSION, '5.0.0', '>=') || 
        !class_exists('COM', false)) {
      return false;
    }
    // Note(ppershing): there is no documentation on return value
    // bad construction of COM object, but false seems to be the case.
    $tmp = new COM(self::CRYPTOAPI_COM);
    return ($tmp !== false);
  }


  /**
   * Returns a securely generated random bytes from windows cryptoapi.
   *
   * Warning: use SecureRandom::random() instead
   *
   * @param int $count number of random bytes to be generated
   *
   * @returns bytes|false generated bytes or false on error
   */
  public function randomBytes($count) {
    Preconditions::check(is_int($count));
    Preconditions::check($count > 0);

    if (!self::isAvailable()) {
      return false;
    }

    $util = new COM(self::CRYPTOAPI_COM);
    $base64 = $util->GetRandom($count, 0); // default encoding is base64
    $output = base64_decode($base64, true); // be strict
    return $output;
  }
}
